<?php
/**
* MenusModel.php
* Contains CRUD settings for menu management.
*/

class MenusModel
{
    /**
    * SelectAllMenus
    * Fetching the menus from the database.
    *
    * @return Array $menuArray containing menu references 
    *
    */
    public function GetAllMenus()
    {
        $menus = array();

        require_once("Menu.php");
        require_once "../" . Settings::DBPath();
        $DB = new Database();

        if ( $DB->Connect() )
        {
            $stmt = $DB->Prepare( "SELECT MenuID, Name FROM menus" );
            $stmt->execute();
            $stmt->bind_result( $menuID, $name );

            while ( $stmt->fetch() ) 
            {
                $menus[] = new Menu( $menuID, $name );
            }

            $stmt->close();

            $DB->Close();
        }

        return $menus;
    }

    public function GetMenu( $id )
    {
        $menus = NULL;

        require_once("Menu.php");
        require_once "../" . Settings::DBPath();
        $DB = new Database();

        if ( $DB->Connect() )
        {
            $stmt = $DB->Prepare( "SELECT MenuID, Name FROM menus WHERE MenuID = ?" );
            $stmt->bind_param( "i", $id );
            $stmt->execute();
            $stmt->bind_result( $menuID, $name );
            $stmt->fetch();
            
            $menu = new Menu( $menuID, $name );

            $stmt->close();

            $DB->Close();
        }

        return $menu;
    }
    
    /**
    * SaveMenu
    * Saves a menu.
    *
    * @param string $name The Name input witch the function saves to the database.
    *
    */
    public function AddMenu( $name )
    {
        require_once "../" . Settings::DBPath();
        $DB = new Database();

        if ( $DB->Connect() )
        {
            $stmt = $DB->Prepare( "INSERT INTO menus ( Name ) VALUES( ? )" );
            $stmt->bind_param( "s", $name );
            $stmt->execute();
            $stmt->close();

            $DB->Close();

            return true;
        }

        return false;
    }

    /**
    * RemoveMenu
    * Remove a menu.
    *
    * @param int $menuID The ID witch the function need to know which menu to delete.
    *
    */
    public function RemoveMenu( $menuID )
    {
        require_once "../" . Settings::DBPath();
        $DB = new Database();

        if ( $DB->Connect() )
        {
            $stmt = $DB->Prepare( "DELETE FROM menus WHERE MenuID = ?" );
            $stmt->bind_param( "i", $menuID );
            $stmt->execute();
            $stmt->close();

            $DB->Close();

            return true;
        }

        return false;
    }
    
    public function SaveMenu( $menuName, $menuID )
    {
        require_once "../" . Settings::DBPath();
        $DB = new Database();

        if ( $DB->Connect() )
        {
            $stmt = $DB->Prepare( "UPDATE menus SET Name= ? WHERE MenuID = ?" );
            $stmt->bind_param( "si", $menuName, $menuID );
            $stmt->execute();
            $stmt->close();

            $DB->Close();

            return true;
        }

        return false;
    }
    
	public function GetPages()
	{
		require_once "../" . Settings::DBPath();
        $DB = new Database();

		$pages = array();
		
        if ( $DB->Connect() )
        {
            $stmt = $DB->Prepare( "SELECT PageID, Title FROM pages" );
            $stmt->execute();
			$stmt->bind_result( $row_pageID, $row_title );
			
			while ( $stmt->fetch() )
			{
				$pages[] = array(   'id' => $row_pageID,
									'title' => $row_title );
			}
			
            $stmt->close();
            
            $DB->Close();
        }

        return $pages;
	}
    
    // Check whether a path name already exists.
    public function PathNameExists( $checkName )
    {
        require_once "../" . Settings::DBPath();
        $DB = new Database();
    
        if ( $DB->Connect() )
        {
            $exists = false;
            
            $stmt = $DB->Prepare( "SELECT COUNT( PathID ) FROM paths WHERE Name = ?" );
            $stmt->bind_param( "s", $checkName );
            $stmt->execute();
            $stmt->bind_result( $row_pathCount );
            $stmt->fetch();
            
            // NOTE: DZEMALS AFFECTED ROWS KAJKAR FUCKED UP
            $exists = $row_pathCount > 0;
            
            $stmt->close();
            
            $DB->Close();
        }
        
        return $exists;
    }
    
    public function AddPath( $text, $menuID, $pageID )
    {
        require_once "../" . Settings::DBPath();
        $DB = new Database();
    
        if ( $DB->Connect() )
        {
            // Set menu items parent.
            $parent = 0;
        
            // Set a unique path name.
            $origName = urlencode( str_replace(' ', '_', trim( $text ) ) );
            $name = $origName;
            $nameExistsCount = 2;
            while ( $this->PathNameExists( $name ) )
            {
                $name = $origName . "-" .  $nameExistsCount;
                $nameExistsCount++;
            }
        
            // Identify and set order.
            $stmt = $DB->Prepare( "SELECT MAX( paths.Order ) FROM paths WHERE Parent = ? AND MenuID = ?" );
            $stmt->bind_param( "ii", $parent, $menuID );
            $stmt->execute();
            $stmt->bind_result( $row_order );
            $stmt->fetch();
            $stmt->close();
            
            $order = 1 + $row_order;
        
            // Store menu item.
            $stmt = $DB->Prepare( "INSERT INTO paths ( Name, Text, paths.Order, Parent, MenuID, PageID ) VALUES( ?, ?, ?, ?, ?, ? )" );
            $stmt->bind_param( "ssiiii", $name, $text, $order, $parent, $menuID, $pageID );
            $stmt->execute();
            $stmt->close();
            
            $DB->Close();

            return true;
        }

        return false;
    }
    
    public function GetPaths( $menuID, $parentID = 0 )
	{
		require_once "../" . Settings::DBPath();
        $DB = new Database();

		$paths = array();
		
        if ( $DB->Connect() )
        {
            $stmt = $DB->Prepare( "SELECT PathID, Name, Text, paths.Order, Parent, MenuID, PageID FROM paths WHERE MenuID = ? AND Parent = ? ORDER BY paths.Order ASC" );
            $stmt->bind_param( "ii", $menuID, $parentID );
            $stmt->execute();
			$stmt->bind_result( $row_pathID, $row_name, $row_text, $row_order, $row_parent, $row_menuID, $row_pageID );
			
			while ( $stmt->fetch() )
			{
				$paths[] = array(   'id' => $row_pathID,
									'name' => $row_name,
                                    'text' => $row_text,
                                    'order' => $row_order,
                                    'parent' => $row_parent,
                                    'menuID' => $row_menuID,
                                    'pageID' => $row_pageID,
                                    'children' => NULL );
			}
			
            $stmt->close();
            
            $DB->Close();
        }

        $pathsCount = count( $paths );
        for( $i = 0; $i < $pathsCount; $i++ )
        {
            $paths[$i]['children'] = $this->GetPaths( $paths[$i]['menuID'], $paths[$i]['id'] );
        }
        
        return $paths;
	}
    
    public function GetPath( $pathID )
    {
		require_once "../" . Settings::DBPath();
        $DB = new Database();

		$path = NULL;
		
        if ( $DB->Connect() )
        {
            $stmt = $DB->Prepare( "SELECT PathID, Name, Text, paths.Order, Parent, MenuID, PageID FROM paths WHERE PathID = ?" );
            $stmt->bind_param( "i", $pathID );
            $stmt->execute();
			$stmt->bind_result( $row_pathID, $row_name, $row_text, $row_order, $row_parent, $row_menuID, $row_pageID );
			$stmt->fetch();

            $path = array(  'id' => $row_pathID,
                            'name' => $row_name,
                            'text' => $row_text,
                            'order' => $row_order,
                            'parent' => $row_parent,
                            'menuID' => $row_menuID,
                            'pageID' => $row_pageID );
			
            $stmt->close();
            
            $DB->Close();
        }

        return $path;
    }
    
    public function SaveMenuItem( $pathID, $name, $text, $pageID )
    {
        require_once "../" . Settings::DBPath();
        $DB = new Database();

        if ( $DB->Connect() )
        {
            $stmt = $DB->Prepare( "UPDATE paths SET Name = ?, Text = ?, PageID = ? WHERE PathID = ?" );
            $stmt->bind_param( "ssii", $name, $text, $pageID, $pathID );
            $stmt->execute();
            $stmt->close();

            $DB->Close();

            return true;
        }

        return false;
    }
    
    public function DeleteMenuItem( $pathID )
    {
        $path = $this->GetPath( $pathID );
        
        require_once "../" . Settings::DBPath();
        $DB = new Database();

        if ( $DB->Connect() )
        {
            $subsequentItems = array();
            
            // Get affected subsequential menu items.
            $stmt = $DB->Prepare( "SELECT PathID FROM paths WHERE MenuID = ? AND Parent = ? AND paths.Order > ? ORDER BY paths.Order ASC" );
            $stmt->bind_param( "iii", $path['menuID'], $path['parent'], $path['order'] );
            $stmt->execute();
            $stmt->bind_result( $row_pathID );
            
            while ( $stmt->fetch() )
            {
                $subsequentItems[] = $row_pathID;
            }
            
            $stmt->close();
        
            // Remove indicated menu item.
            $stmt = $DB->Prepare( "DELETE FROM paths WHERE PathID = ?" );
            $stmt->bind_param( "i", $pathID );
            $stmt->execute();
            $stmt->close();

            // Move up subsequential items in order.            
            foreach ( $subsequentItems as $itemID )
            {
                $stmt = $DB->Prepare( "UPDATE paths SET paths.Order = (paths.Order - 1) WHERE PathID = ?" );
                $stmt->bind_param( "i", $itemID );
                $stmt->execute();
                $stmt->close();
            }
            
            $DB->Close();

            return true;
        }

        return false;
    }
    
    public function MoveUpMenuItem( $pathID )
    {
        $path = $this->GetPath( $pathID );
    
        if ( ! $this->IsFirstInOrder( $path ) )
        {
            require_once "../" . Settings::DBPath();
            $DB = new Database();

            if ( $DB->Connect() )
            {
                $previousItem = $this->GetPreviousMenuItem( $path );
                
                // Switch order between affected items.
                $stmt = $DB->Prepare( "UPDATE paths SET paths.Order = ? WHERE PathID = ?" );
                $stmt->bind_param( "ii", $previousItem['order'], $path['id'] );
                $stmt->execute();
                $stmt->close();
                
                $stmt = $DB->Prepare( "UPDATE paths SET paths.Order = ? WHERE PathID = ?" );
                $stmt->bind_param( "ii", $path['order'], $previousItem['id'] );
                $stmt->execute();
                $stmt->close();
                
                $DB->Close();

                return true;
            }
        }

        return false;
    }
    
    public function MoveDownMenuItem( $pathID )
    {
        $status = false;
        $path = $this->GetPath( $pathID );
        
        if ( ! $this->IsLastInOrder( $path ) )
        {
            require_once "../" . Settings::DBPath();
            $DB = new Database();

            if ( $DB->Connect() )
            {
                $nextItem = $this->GetNextMenuItem( $path );
                
                // Switch order between affected items.
                $stmt = $DB->Prepare( "UPDATE paths SET paths.Order = ? WHERE PathID = ?" );
                $stmt->bind_param( "ii", $nextItem['order'], $path['id'] );
                $stmt->execute();
                $stmt->close();
                
                $stmt = $DB->Prepare( "UPDATE paths SET paths.Order = ? WHERE PathID = ?" );
                $stmt->bind_param( "ii", $path['order'], $nextItem['id'] );
                $stmt->execute();
                $stmt->close();
                
                $status = true;
                
                $DB->Close();
            }
        }
        
        return $status;
    }
    
    public function MoveInMenuItem( $pathID )
    {
        $path = $this->GetPath( $pathID );
        
        if ( ! $this->IsFirstInOrder( $path ) )
        {
            require_once "../" . Settings::DBPath();
            $DB = new Database();
            
            if ( $DB->Connect() )
            {
                $subsequentItems = array();
                
                $previousItem = $this->GetPreviousMenuItem( $path );
                $lastChild = $this->GetLastChild( $previousItem );
                $newOrder = ( $lastChild !== NULL ) ? $lastChild['order'] + 1 : 1;
                
                // Get affected subsequential menu items.
                $stmt = $DB->Prepare( "SELECT PathID FROM paths WHERE MenuID = ? AND Parent = ? AND paths.Order > ? ORDER BY paths.Order ASC" );
                $stmt->bind_param( "iii", $path['menuID'], $path['parent'], $path['order'] );
                $stmt->execute();
                $stmt->bind_result( $row_pathID );
                
                while ( $stmt->fetch() )
                {
                    $subsequentItems[] = $row_pathID;
                }
                
                $stmt->close();
            
                // Change targeted menu items parent to previous sibling.
                $stmt = $DB->Prepare( "UPDATE paths SET Parent = ?, paths.Order = ? WHERE PathID = ?" );
                $stmt->bind_param( "iii", $previousItem['id'], $newOrder, $path['id'] );
                $stmt->execute();
                $stmt->close();
                
                // Move up subsequential items in order.            
                foreach ( $subsequentItems as $itemID )
                {
                    $stmt = $DB->Prepare( "UPDATE paths SET paths.Order = (paths.Order - 1) WHERE PathID = ?" );
                    $stmt->bind_param( "i", $itemID );
                    $stmt->execute();
                    $stmt->close();
                }
                
                $DB->Close();
            }
        }
        
        return false;
    }
    
    public function MoveOutMenuItem( $pathID )
    {
		$path = $this->GetPath( $pathID );
        
        if ( ! $this->IsRootLevel( $path ) )
        {
            require_once "../" . Settings::DBPath();
            $DB = new Database();
            
            if ( $DB->Connect() )
            {
				$subsequentItems = array();
				
				// Get menu items parent data.
				$parent = $this->GetPath( $path['parent'] );
				
				// Get new menu item order.
				$stmt = $DB->Prepare( "SELECT MAX( paths.Order ) FROM paths WHERE MenuID = ? AND Parent = ? ORDER BY paths.Order ASC" );
                $stmt->bind_param( "ii", $path['menuID'], $parent['parent'] );
                $stmt->execute();
                $stmt->bind_result( $row_order );
				$stmt->fetch();
				$newOrder = $row_order + 1;
                $stmt->close();

                // Get affected subsequential menu items.
                $stmt = $DB->Prepare( "SELECT PathID FROM paths WHERE MenuID = ? AND Parent = ? AND paths.Order > ? ORDER BY paths.Order ASC" );
                $stmt->bind_param( "iii", $path['menuID'], $path['parent'], $path['order'] );
                $stmt->execute();
                $stmt->bind_result( $row_pathID );
                
                while ( $stmt->fetch() )
                {
                    $subsequentItems[] = $row_pathID;
                }
                
                $stmt->close();
           
		        // Update targeted menu item data.
                $stmt = $DB->Prepare( "UPDATE paths SET paths.Order = ?, Parent = ? WHERE PathID = ?" );
                $stmt->bind_param( "iii", $newOrder, $parent['parent'], $path['id'] );
                $stmt->execute();
				$stmt->close();
                
                // Move up subsequential items in order.            
                foreach ( $subsequentItems as $itemID )
                {
                    $stmt = $DB->Prepare( "UPDATE paths SET paths.Order = (paths.Order - 1) WHERE PathID = ?" );
                    $stmt->bind_param( "i", $itemID );
                    $stmt->execute();
                    $stmt->close();
                }
                
                $DB->Close();
            }
        }
        
        return false;
    }
    
    ####################### PRIVATE #################################################
    
    private function GetLastChild( $parent )
    {
        $lastChild = NULL;
        
        require_once "../" . Settings::DBPath();
        $DB = new Database();

        if ( $DB->Connect() )
        {
            $stmt = $DB->Prepare( "SELECT PathID FROM paths WHERE paths.Order = ( SELECT MAX( paths.Order ) FROM paths WHERE MenuID = ? AND Parent = ? ) AND MenuID = ? AND Parent = ?" );
            $stmt->bind_param( "iiii", $parent['menuID'], $parent['id'], $parent['menuID'], $parent['id'] );
            $stmt->execute();
            $stmt->bind_result( $row_id );
            $stmt->fetch();
            $lastChild = $this->GetPath( $row_id );
            $stmt->close();
            
            $DB->Close();
        }
        return $lastChild;
    }
    
    private function IsFirstInOrder( $path )
    {
        if ( $path['order'] == 1 )
        {
            return true;
        }
        
        return false;
    }
    
    private function IsLastInOrder( $path )
    {
        require_once "../" . Settings::DBPath();
        $DB = new Database();

        if ( $DB->Connect() )
        {
            $stmt = $DB->Prepare( "SELECT MAX( paths.Order ) FROM paths WHERE MenuID = ? AND Parent = ?" );
            $stmt->bind_param( "ii", $path['menuID'], $path['parent'] );
            $stmt->execute();
            $stmt->bind_result( $row_order );
            $stmt->fetch();
            $lastItemOrder = $row_order;
            $stmt->close();
            
            $DB->Close();
            
            if ( $lastItemOrder === $path['order'] )
            {
                return true;
            }
        }
        
        return false;
    }
    
    private function GetPreviousMenuItem( $path )
    {
        $prevPathID = NULL;
        
        require_once "../" . Settings::DBPath();
        $DB = new Database();

        if ( $DB->Connect() )
        {
            $prevOrder = $path['order'] - 1;
        
            $stmt = $DB->Prepare( "SELECT PathID FROM paths WHERE MenuID = ? AND Parent = ? AND paths.Order = ?" );
            $stmt->bind_param( "iii", $path['menuID'], $path['parent'], $prevOrder );
            $stmt->execute();
            $stmt->bind_result( $row_pathID );
            $stmt->fetch();
            $prevPathID = $this->GetPath( $row_pathID );
            $stmt->close();
            
            $DB->Close();
        }
        
        return $prevPathID;
    }
    
    private function GetNextMenuItem( $path )
    {
        $nextPathID = NULL;
        
        require_once "../" . Settings::DBPath();
        $DB = new Database();

        if ( $DB->Connect() )
        {
            $nextOrder = $path['order'] + 1;
        
            $stmt = $DB->Prepare( "SELECT PathID FROM paths WHERE MenuID = ? AND Parent = ? AND paths.Order = ?" );
            $stmt->bind_param( "iii", $path['menuID'], $path['parent'], $nextOrder );
            $stmt->execute();
            $stmt->bind_result( $row_pathID );
            $stmt->fetch();
            $nextPathID = $this->GetPath( $row_pathID );
            $stmt->close();
            
            $DB->Close();
        }
        
        return $nextPathID;
    }
    
    private function IsRootLevel( $path )
    {
        require_once "../" . Settings::DBPath();
        $DB = new Database();
        
		$isRoot = false;
        if ( $DB->Connect() )
        {
            $stmt = $DB->Prepare( "SELECT Parent FROM paths WHERE PathID = ? " );
            $stmt->bind_param( "i", $path['id'] );
            $stmt->execute();
            $stmt->bind_result( $row_parent );
            $stmt->fetch();
            
			$isRoot = 0 == $row_parent;
			
			$stmt->close();
            
            $DB->Close();
        }
        
        return $isRoot;
    }
}
